/*
* ========== Copyright Header Begin ==========================================
*
* OpenSPARC T1 Processor File: Rstzipi.h
* Copyright (c) 2006 Sun Microsystems, Inc.  All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES.
*
* The above named program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License version 2 as published by the Free Software Foundation.
*
* The above named program is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*
* ========== Copyright Header End ============================================
*/

#ifndef _RSTZIP_H
#define _RSTZIP_H

// File: rstzip.H
//
// Adapted loosely from Qi Min's previous work, with major input from
// Russell Quong.
//
// Send complaints to: klf@eng.sun.com

#define DBGFP stdout

#include <ctype.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <stdint.h>

#include "rz_insttypes.h"

#include "rstf.h"
#include "zlib.h"
#include "hash.h"
#include "pstate.h"
#include "ValueCache.h"


#ifdef COM_DEBUG
#define comDebug( str ) fprintf( stderr, str )
#define comDebugP( str, var )   fprintf( stderr, str, var )
#else
#define comDebug( str )
#define comDebugP( str, var )
#endif

#ifdef DEC_DEBUG
#define decDebug( str ) fprintf( stderr, str )
#define decDebugP( str, var )   fprintf( stderr, str, var )
#else
#define  decDebug( str )
#define  decDebugP( str, var )
#endif

// static int32_t carry_ea(int32_t ih, int32_t ea_valid) { // changed - 20040210 (vp)
static int32_t carry_ea(uint32_t iw, int32_t ea_valid) {
    // return (ea_valid == 1 && ih_ispcrelcti(ih) == 0); // replaced - 20040210 (vp)
    return (ea_valid == 1 && !rz_is_pc_relative_cti(iw));
}

#define MAX(a, b) (((a) > (b)) ? (a) : (b))

enum {
    MAX_CHUNKSIZE     = 1000,
    CHECKSUM_FREQ     = 16,

    INSTR_FOLLOWS     = 0,
    NONINSTR_FOLLOWS  = 1,

    CHUNKSIZE_RES     = 10,

    NEW_RTYPES_START  = 160,
    NEW_RTYPES        = 13,

    RSTZIP_MAX_CONTEXTS = 8192
};

enum {
    z_HEADER_T = NEW_RTYPES_START,
    z_FOOTER_T,

    z_INSTR_T,      // compressed chunk
    zL_INSTR_T,     // compressed loop chunk

    z0_PAVADIFF_T,  // compresed pavadiff at cache index n
    z1_PAVADIFF_T,
    z2_PAVADIFF_T,
    z3_PAVADIFF_T,
    z4_PAVADIFF_T,
    z5_PAVADIFF_T,
    z6_PAVADIFF_T,
    z7_PAVADIFF_T,

    z_CCR_T,           // condition-code register
    z_REGID_T,         // register id
    z_REGVAL_8_T,      // 1-byte register value
    z_REGVAL_16_T,     // 2-byte register value
    z_REGVAL_32_T,     // 4-byte register value
    z_REGVAL_64_T,     // 8-byte register value
    z_REGIDX_T,        // 2-byte register index
    z_VALUE_MINUS1_T,  // These are the literal values -1 to 8
    z_VALUE_0_T,
    z_VALUE_1_T,
    z_VALUE_2_T,
    z_VALUE_3_T,
    z_VALUE_4_T,
    z_VALUE_5_T,
    z_VALUE_6_T,
    z_VALUE_7_T,
    z_VALUE_8_T,
    z_REGPAIR_T,        // indicates that the next two register values should be
    // merged into a single RST regval record.
    z_LAST_T
};

enum {
    OFFSET_8BITS_IDX          = 0,
    OFFSET_8BITS_RESERVED_IDX = 1,
    OFFSET_16BITS_IDX         = 2,
    OFFSET_32BITS_IDX         = 3,
    OFFSET_64BITS_IDX         = 4,

    RESERVED_OFFSET_8BITS     = -125,  // (0xffffff83)
    RESERVED_OFFSET_16BITS    = -123,  // (0xffffff85)
    RESERVED_OFFSET_32BITS    = 123,   // (0x7b)
    RESERVED_OFFSET_64BITS    = 125,   // (0x7d)

    NUM_RESERVED_OFFSETS      = 4,

    PADDING_SIZE0             = 2,
    FILENAME_STRING_SIZE      = 256,
    DATE_STRING_SIZE          = 32,
    HASH_FUNC_STRING_SIZE     = 64,
    PADDING_SIZE1             = 630
};

enum {
    NOTUSED      = 0x80,
    EA_VALID     = 0x40,
    TR           = 0x20,
    NOTUSED2     = 0x10,
    PR           = 0x08,
    BT           = 0x04,
    AN           = 0x02,
    RSRVD_CMPRSS = 0x01
};

typedef uint8_t flags_t;

class RstzipBase {
public:
    // Options
    bool compress;                // Set to 1 to compress, 0 to decompress
    uint8_t checksum_freq;        // Add checksum every n chunks (default=16)

    // stats
    uint32_t max_chunksize;
    uint64_t total_instr;
    uint64_t total_noninstr;
    uint64_t total_loop_chunk;
    uint64_t total_nonloop_chunk;
    uint64_t total_zpavadiff;
    uint64_t total_pavadiff;
    uint64_t zero_offset_count;
    uint64_t offset_count[OFFSET_64BITS_IDX + 1];
    uint64_t chunksize_count[CHUNKSIZE_RES];

    RstzipHash hash;
    RstzipPavadiffCache pava_cache;

    // Constructor (initialize file footer variables).
    RstzipBase() {
        max_chunksize = 0;
        total_instr = 0;
        total_noninstr = 0;
        total_loop_chunk = 0;
        total_nonloop_chunk = 0;
        total_zpavadiff = 0;
        total_pavadiff = 0;
        zero_offset_count = 0;

        memset(offset_count, 0, (OFFSET_64BITS_IDX + 1) * sizeof(uint64_t));
        memset(chunksize_count, 0, CHUNKSIZE_RES * sizeof(uint64_t));

        print_chunk_chksm = 0;
        print_chunk_chunk_counter = 0;

        compress = -1;
        checksum_freq = CHECKSUM_FREQ;
    }  // RstzipBase::RstzipBase()

protected:
    // header info
    uint8_t rtype;
    uint16_t num_instr;                // number of instructions in chunk
    uint64_t pc_start;                 // pc of 1st instruction in chunk

    // data buffers
    uint32_t instr_buf[MAX_CHUNKSIZE];         // instruction words in chunk
    uint64_t ea_buf[MAX_CHUNKSIZE];            // ea of ld/st+cti instructions
    flags_t flags_buf[MAX_CHUNKSIZE];          // flags for each instruction
    uint8_t noninstr_count_buf[MAX_CHUNKSIZE]; // counts of non-instr chunks
    int32_t lastRegIdBuf[MAX_CHUNKSIZE];           // the regid observed in an instruction
    rstf_instrT noninstr_buf[MAX_CHUNKSIZE];   // non-instr records in chunk

    // misc info
    int32_t num_ea;                        // number of ld/st+cti instrs in chunk
    int32_t num_noninstr;                  // non-instruction records in chunk
    int32_t num_total;                     // num_instr + num_noninstr
    int32_t num_noninstr_count;            // size of noninstr_count_buf[]


    // flags support
    flags_t set_flags(flags_t* flags, int32_t mask, int32_t val) {
        flags_t flgs = *flags;

        if (val) {
            flgs = flgs | mask;
        } else {
            flgs = flgs & ~mask;
        }

        *flags = flgs;

        return flgs;
    }  // RstzipBase::set_flags()

    uint64_t compute_checksum64() {
        int32_t i;
        uint64_t chksm;

        chksm = rtype + num_instr + pc_start;

        for (i = 0; i < num_instr; i++) {
            chksm += instr_buf[i] + flags_buf[i];
        }

        for (i = 0; i < num_ea; i++) {
            chksm += ea_buf[i];
        }

        for (i = 0; i < num_noninstr; i++) {
            chksm += noninstr_buf[i].rtype;
        }

        for (i = 0; i < num_noninstr_count; i++) {
            chksm += noninstr_count_buf[i];
        }

        return chksm;
    }  // RstzipBase::compute_checksum64()

    int32_t check_buffersize(int32_t nrecs) {
        if (nrecs < MAX_CHUNKSIZE) {
            return MAX_CHUNKSIZE - nrecs;
        }

        return 0;
    }  // RstzipBase::check_buffersize()

    int32_t get_flags(flags_t flags, int32_t mask) {
        return ((flags & mask) ? 1 : 0);
    }  // RstzipBase::get_flags()

    uint64_t print_chunk_chksm;
    uint64_t print_chunk_chunk_counter;

    void print_chunk(FILE* fp) {
        int32_t i;

        if (rtype != 0) {
            fprintf(fp, "[BOC] rtype=%d\n", rtype);
            fprintf(fp, "num_instr=%d\n", num_instr);
            fprintf(fp, "pc_start=0x%llx\n", pc_start);
            fprintf(fp, "\n");
            fprintf(fp, "num_ea=%d\n", num_ea);
            fprintf(fp, "num_noninstr=%d\n", num_noninstr);
            fprintf(fp, "num_total=%d\n", num_total);
            fprintf(fp, "num_noninstr_count=%d\n", num_noninstr_count);
            fprintf(fp, "\n");

            for (i = 0; i < num_instr; i++) {
                fprintf(fp, "(%x)\n", instr_buf[i]);
            }
            fprintf(fp, "\n");

            for (i = 0; i < num_instr; i++) {
                fprintf(fp, "(%02x)\n", flags_buf[i]);
            }
            fprintf(fp, "\n");

            for (i = 0; i < num_ea; i++) {
                fprintf(fp, "(0x%llx)\n", ea_buf[i]);
            }
            fprintf(fp, "\n");

            for (i = 0; i < num_noninstr_count; i++) {
                fprintf(fp, "%d ", noninstr_count_buf[i]);
            }
            fprintf(fp, "\n\n");

            for (i = 0; i < num_noninstr; i++) {
                print_rstrec(fp, &noninstr_buf[i]);
            }
            fprintf(fp, "\n");

            if (checksum_freq != 0) {
                print_chunk_chksm += compute_checksum64();
                print_chunk_chunk_counter++;

                if (print_chunk_chunk_counter % checksum_freq == 0) {
                    fprintf(fp, "checksum=%016llx\n", print_chunk_chksm);
                    print_chunk_chksm = 0;
                }
            }

            fprintf(fp, "[EOC] max_chunksize=%d\n", max_chunksize);
        }
    }  // RstzipBase::print_chunk()

    void print_rstbuf(FILE* fp, rstf_instrT buf[], int32_t num) {
        int32_t i;

        for (i = 0; i < num; i++) {
            print_rstrec(fp, &buf[i]);
        }
        fprintf(fp, "\n");
    }  // RstzipBase::print_rstbuf()

    void print_rstrec(FILE* fp, rstf_instrT* rst) {
        switch (rst->rtype) {
        case RSTHEADER_T:
            fprintf(fp, "rstheader\n");
            break;
        case INSTR_T:
            fprintf(fp, "[0x%llx] ", rst->pc_va);
            // fprintDiss(fp, rst->instr, rst->pc_va);
            fprintf(fp, "\n");
            break;
#if 0
        case ASI_T:
            fprintf(fp, "asi\n");
            break;
#endif
        case MEMVAL_T:
            fprintf( fp, "memval\n" );
            break;
        case TLB_T:
            fprintf(fp, "tlb\n");
            break;
        case THREAD_T:
            fprintf(fp, "thread\n");
            break;
        case TRAP_T:
            fprintf(fp, "trap\n");
            break;
        case TRAPEXIT_T:
            fprintf(fp, "trapexit\n");
            break;
        case REGVAL_T:
            fprintf(fp, "regval\n");
            break;
        case TIMESTAMP_T:
            fprintf(fp, "timestamp\n");
            break;
        case PROCESS_T:
            fprintf(fp, "process\n");
            break;
        case DMA_T:
            fprintf(fp, "dma\n");
            break;
        case STRDESC_T:
            fprintf(fp, "strdesc\n");
            break;
        case LEFTDELIM_T:
            fprintf(fp, "leftdelim\n");
            break;
        case RIGHTDELIM_T:
            fprintf(fp, "rightdelim\n");
            break;
        case PREG_T:
            fprintf(fp, "preg\n");
            break;
        case PHYSADDR_T:
            fprintf(fp, "physaddr\n");
            break;
        case PAVADIFF_T:
#if 0
            fprintf(fp, "pavadiff\n");
#else
            rstf_pavadiffT rst_pava;

            memcpy(&rst_pava, rst, sizeof(rstf_unionT));
            fprintf(fp, "pavadiff: icontext=%d dcontext=%d pc_pa_va=0x%016llx ea_pa_va=0x%016llx\n",
                    rst_pava.icontext, rst_pava.dcontext, rst_pava.pc_pa_va, rst_pava.ea_pa_va);
#endif
            break;
        case NULLREC_T:
            fprintf(fp, "nullrec\n");
            break;
        case STRCONT_T:
            fprintf(fp, "strcont\n");
            break;
        case FILEMARKER_T:
            fprintf(fp, "filemarker\n");
            break;
        case PATCH_T:
            fprintf(fp, "patch\n");
            break;
        case STATUS_T:
            fprintf(fp, "status\n");
            break;
#if 0
        case SNOOP_T:
            fprintf(fp, "snoop\n");
            break;
#endif
        case z_HEADER_T:
            fprintf(fp, "file header\n");
            break;
        case z_FOOTER_T:
            fprintf(fp, "file footer\n");
            break;
        case z_INSTR_T:
            fprintf(fp, "compressed header\n");
            break;
        case zL_INSTR_T:
            fprintf(fp, "compressed loop header\n");
            break;
        case z0_PAVADIFF_T:
            fprintf(fp, "compressed pavadiff [0]\n");
            break;
        case z1_PAVADIFF_T:
            fprintf(fp, "compressed pavadiff [1]\n");
            break;
        case z2_PAVADIFF_T:
            fprintf(fp, "compressed pavadiff [2]\n");
            break;
        case z3_PAVADIFF_T:
            fprintf(fp, "compressed pavadiff [3]\n");
            break;
        case z4_PAVADIFF_T:
            fprintf(fp, "compressed pavadiff [4]\n");
            break;
        case z5_PAVADIFF_T:
            fprintf(fp, "compressed pavadiff [5]\n");
            break;
        case z6_PAVADIFF_T:
            fprintf(fp, "compressed pavadiff [6]\n");
            break;
        case z7_PAVADIFF_T:
            fprintf(fp, "compressed pavadiff [7]\n");
            break;
        case z_CCR_T:
            fprintf(fp, "Condition Code Register\n" );
            break;
        case z_REGID_T:
            fprintf(fp, "Integer Register Id\n" );
            break;
        case z_REGVAL_8_T:
            fprintf(fp, "1-byte register value\n" );
            break;
        case z_REGVAL_16_T:
            fprintf(fp, "2-byte register value\n" );
            break;
        case z_REGVAL_32_T:
            fprintf(fp, "4-byte register value\n" );
            break;
        case z_REGVAL_64_T:
            fprintf(fp, "8-byte register value\n" );
            break;
        case z_REGIDX_T:
            fprintf(fp, "register value cache index\n" );
            break;
        case z_VALUE_MINUS1_T:
            fprintf(fp, "integer value -1\n" );
            break;
        case z_VALUE_0_T:
            fprintf(fp, "integer value 0\n" );
            break;
        case z_VALUE_1_T:
            fprintf(fp, "integer value 1\n" );
            break;
        case z_VALUE_2_T:
            fprintf(fp, "integer value 2\n" );
            break;
        case z_VALUE_3_T:
            fprintf(fp, "integer value 3\n" );
            break;
        case z_VALUE_4_T:
            fprintf(fp, "integer value 4\n" );
            break;
        case z_VALUE_5_T:
            fprintf(fp, "integer value 5\n" );
            break;
        case z_VALUE_6_T:
            fprintf(fp, "integer value 6\n" );
            break;
        case z_VALUE_7_T:
            fprintf(fp, "integer value 7\n" );
            break;
        case z_VALUE_8_T:
            fprintf(fp, "integer value 8\n" );
            break;
        case z_REGPAIR_T:
            fprintf(fp, "register pair marker\n" );
            break;

        default:
            int32_t range = 1;
            uint8_t* c_ptr = (uint8_t*) (rst - range);

            for (int32_t i = range; i > 0; i--) {
                for (size_t j = 0; j < sizeof(rstf_instrT); j++) {
                    fprintf(fp, "%02x", *c_ptr);
                    c_ptr++;
                }
                fprintf(fp, " -> -%d\n", i);
            }

            for (size_t j = 0; j < sizeof(rstf_instrT); j++) {
                fprintf(fp, "%02x", *c_ptr);
                c_ptr++;
            }
            fprintf(fp, " -> Unknown rtype (%02x)\n", rst->rtype);

            for (int32_t i = 1; i <= range; i++) {
                for (size_t j = 0; j < sizeof(rstf_instrT); j++) {
                    fprintf(fp, "%02x", *c_ptr);
                    c_ptr++;
                }
                fprintf(fp, " -> +%d\n", i);
            }

            exit(2);
        }

        fflush(fp);
    }  // RstzipBase::print_rstrec()

    bool isIntRegCompressable( const rstf_regvalT *r ) const {
        return r->regtype[0] == RSTREG_INT_RT &&
               ( r->regtype[1] == RSTREG_UNUSED_RT || r->regtype[1] == RSTREG_CC_RT || r->regtype[1] == RSTREG_INT_RT );
    }


    bool compressedRegType( int32_t rt ) {

        switch( rt ) {
        case z_CCR_T:
        case z_REGID_T:
        case z_REGVAL_8_T:
        case z_REGVAL_16_T:
        case z_REGVAL_32_T:
        case z_REGVAL_64_T:
        case z_REGIDX_T:
        case z_VALUE_0_T:
        case z_VALUE_1_T:
        case z_VALUE_2_T:
        case z_VALUE_3_T:
        case z_VALUE_4_T:
        case z_VALUE_5_T:
        case z_VALUE_6_T:
        case z_VALUE_7_T:
        case z_VALUE_8_T:
        case z_VALUE_MINUS1_T:
        case z_REGPAIR_T:
            return true;
        default:
            return false;
        }
    }


}; // RstzipBase

// RST decompression class.
class Rstunzip : public RstzipBase {
public:
    Rstunzip() {
        inbuf = NULL;
        inbuf_ptr = NULL;
        outbuf= NULL;
        outbuf_ptr= NULL;

        rstz_decompress_unzipped_recs = 0;
        rstz_decompress_fbytes = 0;
        //rstz_decompress_prev_nrecs = 0;
        unzip_chunk_chksm_sum = 0;
        unzip_chunk_chunk_counter = 0;
        memset(&read_noninstr_rec_rst, 0, sizeof(rstf_unionT));

        icontext = 0;
#ifdef DEC_DEBUG
        iCtr = 1;
#endif
        lastRd = -1;
    }

    ~Rstunzip() {
        //free(inbuf);
        //free(outbuf);
    }

    int32_t rstz_decompress_unzipped_recs;  // new recs unzipped in next chunk in outbuf
    int32_t rstz_decompress_fbytes;         // number of bytes of compressed data fread()
    // Decompress up to nrecs RST records from *infp into buf[].
    // Returns number of records decompressed.  Upon return, *infp will
    // point to the start of the next compressed record.
    int32_t rstz_decompress(uint8_t** zbufptr, rstf_unionT* rstbuf, int32_t nrecs) {
        int32_t total_recs = 0;        // total recs copied to buf[]
        rstf_instrT* buf_ptr = &rstbuf[0].instr;

#if 0
        if (nrecs == 0) {
            return 0;
        }
#endif

        inbuf = *zbufptr;
        outbuf = &rstbuf[0].instr;
        inbuf_ptr = inbuf;
        outbuf_ptr = outbuf;
        rstz_decompress_fbytes = 0;
        rstz_decompress_unzipped_recs = 0;

        total_recs = 0;

        // Unzip chunks from inbuf[] until buf[] cannot take another chunk.
        while (total_recs < nrecs) {
            rstz_decompress_unzipped_recs = unzip_chunk();

            if (num_instr > 0) {
                max_chunksize = MAX((int)max_chunksize, rstz_decompress_unzipped_recs);
#if _DEBUG0
                print_chunk(DBGFP);
                fprintf(DBGFP, "zrecs_o()=%d total_recs=%d nrecs=%d\n\n", zrecs_o(), total_recs, nrecs);
#endif
            }

            total_recs += rstz_decompress_unzipped_recs;
            total_instr += num_instr;
            total_noninstr += rstz_decompress_unzipped_recs - num_instr;

            if (*inbuf_ptr == z_FOOTER_T) {
                break;
            }
        }

        *zbufptr = inbuf_ptr + 1;

        return total_recs;
    }  // Rstunzip::rstz_decompress()

protected:

    // rd-compression member data:
    ValueCache valueCache;
    int32_t lastRd;
#ifdef DEC_DEBUG
    int32_t iCtr;
#endif
    ///////////////////////////////

    uint8_t* inbuf;              // input buffer for (de)compressed traces
    uint8_t* inbuf_ptr;          // always points to current inbuf location
    rstf_instrT* outbuf;         // output buffer for (de)compressed traces
    rstf_instrT* outbuf_ptr;     // always points to current outbuf location

    Pstate pstate;
    int32_t icontext;

    uint64_t unzip_chunk_chksm_sum;
    uint64_t unzip_chunk_chunk_counter;
    // Decompress one chunk from inbuf[] into outbuf[].  Return the
    // number of records decompressed.
    int32_t unzip_chunk() {
        int32_t prerecs, hashval, set;
        uint64_t chksm;
        hash_table_t* table;

        // init some data members
        rtype = 0;
        pc_start = 0;
        num_instr = 0;
        num_ea = 0;
        num_noninstr = 0;
        num_total = 0;
        num_noninstr_count = 0;

        memset(noninstr_count_buf, 0, MAX_CHUNKSIZE * sizeof(uint8_t));

        prerecs = 0;

        // find next compressed chunk rtype
        while ((*inbuf_ptr < z_FOOTER_T || *inbuf_ptr > zL_INSTR_T) && prerecs < MAX_CHUNKSIZE) {
            //print_rstrec(DBGFP, (rstf_instrT*) inbuf_ptr);

            if (*inbuf_ptr == PREG_T) {
                rstf_pregT preg;
                preg.read(inbuf_ptr);
                //pstate[preg.primD].pstate = preg.pstate;
                pstate.pstate = preg.pstate;
            } else if (*inbuf_ptr == PAVADIFF_T) {
                rstf_pavadiffT pavadiff;

                pavadiff.read(inbuf_ptr);
                icontext = pavadiff.icontext;
            }

            write_rst2outbuf(read_noninstr_rec(), 1);
            prerecs++;
        }

        // If there were any non-instruction records before the chunk,
        // just return so the next time unzip_chunk() is called it will
        // start pointing at an instruction record.  This is necessary in
        // case prerecs + num_instr + num_noninstr > nrecs.
        if (prerecs == 0 && *inbuf_ptr != z_FOOTER_T) {
            // inbuf_ptr now points to a compressed chunk rtype
            read_inbuf(&rtype, sizeof(rtype)); // uint8_t
            read_inbuf(&num_instr, sizeof(num_instr));
            num_instr = SWAP_SHORT(num_instr);
            read_inbuf(&pc_start, sizeof(pc_start));
            pc_start = SWAP_LONG(pc_start);

#if 0
            static int32_t chunkNumber = 0;
            fprintf(stderr, "Chunk #%d\n", chunkNumber++ );
            fprintf(stderr, "    decoding %u instructions starting at pc 0x%llx\n", num_instr, pc_start );
#endif

            hashval = hash.hash(pc_start);

            if (rtype <= z_INSTR_T) {    // non-loop rtype
                read_inbuf(instr_buf, num_instr * sizeof(uint32_t));

                //jan {
                //reading byte stream into uint32_t - need to swap_word on little endian
                for (int32_t i = 0; i < num_instr; i++) {
                    instr_buf[i] = SWAP_WORD(instr_buf[i]);
                }
                // jan }

                read_inbuf(flags_buf, num_instr * sizeof(flags_t));
                find_num_ea();
                read_ea(NULL);

                hash.write(pc_start, num_instr, instr_buf, num_ea, ea_buf, hashval);
                total_nonloop_chunk++;
            } else {                     // loop rtype
                set = hash.search(pc_start, num_instr, NULL, hashval);
                if (set == NOT_FOUND) {
                    hash.print_set(hashval);

                    fprintf(stderr, "Error: hash.search() returned NOT_FOUND "
                            "for zL64_INSTR_T, pc=0x%llx num_instr=%d\n",
                            pc_start, num_instr);
                    exit(2);
                }

                table = hash.read(set, hashval);
                memcpy(instr_buf, table->instr_buf, num_instr * sizeof(uint32_t));

                read_inbuf(flags_buf, num_instr * sizeof(flags_t));
                find_num_ea();
                read_ea(table);

                hash.update(num_ea, ea_buf, hashval, set);
                total_loop_chunk++;
            }

            find_num_noninstr_count();
            read_inbuf(noninstr_count_buf, num_noninstr_count * sizeof(uint8_t));
            find_num_noninstr();
#ifdef DEC_DEBUG
            fprintf( stderr, "    nr. non-instructions: %d\n", num_noninstr );
#endif
            read_noninstr_recs();

            if (checksum_freq != 0) {
                unzip_chunk_chksm_sum += compute_checksum64();
                unzip_chunk_chunk_counter++;

                if (unzip_chunk_chunk_counter % checksum_freq == 0) {
                    read_inbuf(&chksm, 8);

                    chksm = SWAP_LONG(chksm); //jan

                    if (chksm != unzip_chunk_chksm_sum) {
                        //hash.print_set(hashval);
                        //print_chunk(stderr);

                        fprintf(stderr,
                                "\nError: checksum inequality (0x%llx != 0x%llx) after record %llu\n",
                                chksm, unzip_chunk_chksm_sum, total_instr + total_noninstr);
                        exit(3);
                    }
                    unzip_chunk_chksm_sum = 0;
                }
            }

            if (num_instr > 0) {
                if (num_instr + num_noninstr < MAX_CHUNKSIZE) {
                    chunksize_count[(num_instr + num_noninstr) / (MAX_CHUNKSIZE / CHUNKSIZE_RES)]++;
                } else {
                    chunksize_count[CHUNKSIZE_RES - 1]++;
                }
            }

            write_chunk();
        }

        return num_instr + num_noninstr + prerecs;
    }  // Rstunzip::unzip_chunk()

    // Build the RST's from instr_buf[], ea_buf[], flags_buf[], etc.,
    // and writes the records in the chunk to *outbuf.  Returns the
    // number of records written.
    int32_t write_chunk() {
        int32_t i, j, ea_cnt, noninstr_cnt, noninstr_buf_cnt;
        bool write_noninstr;
        rstf_instrT rst;

        ea_cnt = 0;
        noninstr_cnt = 0;
        noninstr_buf_cnt = 0;

        for (i = 0; i < num_instr; i++) {
            write_noninstr = false;

            rst.rtype = INSTR_T;

            // rst.notused = get_flags(flags_buf[i], NOTUSED);
            rst.notused = 0;
            get_flags(flags_buf[i], NOTUSED);

            rst.ea_valid = get_flags(flags_buf[i], EA_VALID);
            rst.tr = get_flags(flags_buf[i], TR);

            // rst.notused2 = get_flags(flags_buf[i], NOTUSED2);
            get_flags(flags_buf[i], NOTUSED2);
            rst.hpriv = 0;

            rst.pr = get_flags(flags_buf[i], PR);
            rst.bt = get_flags(flags_buf[i], BT);
            rst.an = get_flags(flags_buf[i], AN);

            rst.reservedCompress = 0; // this field might go away in the future
            int32_t reservedCompress = get_flags(flags_buf[i], RSRVD_CMPRSS);
            // get_flags(flags_buf[i], RSRVD_CMPRSS);

            // rst.ihash = getIHash(instr_buf[i]); // removed - 20040210 (vp)
            rst.cpuid9_6 = 0;
            rst.notused3 = 0;

            rst.instr = instr_buf[i];
            rst.pc_va = pc_start + 4*i;

            // if (carry_ea(rst.ihash, rst.ea_valid)) { // replaced - 20040210 (vp)
            if (carry_ea(rst.instr, rst.ea_valid)) {
                rst.ea_va = ea_buf[ea_cnt];
                ea_cnt++;
            } else if (rst.an == 0 && rz_is_pc_relative_cti(instr_buf[i])) {
                // } else if (rst.an == 0 && ih_ispcrelcti(rst.ihash)) {
                if (rst.bt == 1) {
                    // rst.ea_va = getCtiEa(rst.instr, rst.ihash, rst.pc_va); // replaced 20040210 (vp)
                    if (rz_is_bpr(rst.instr)) {
                        rst.ea_va = rst.pc_va+BPR_DISP(rst.instr);
                    } else if (rz_is_bicc(rst.instr)||rz_is_fbfcc(rst.instr)) {
                        rst.ea_va = rst.pc_va+Bicc_DISP(rst.instr);
                    } else if (rz_is_bpcc(rst.instr)||rz_is_fbpfcc(rst.instr)) {
                        rst.ea_va = rst.pc_va+BPcc_DISP(rst.instr);
                    } else if (rz_is_call(rst.instr)) {
                        rst.ea_va = rst.pc_va+CALL_DISP(rst.instr);
                    } else {
                        fprintf(stderr, "Unknown branch type (rst.bt==1, instr word=%08x)\n", rst.instr);
                    }

                    if (pstate.getField(PSTATE_AM) == 1) {
                        rst.ea_va &= 0xffffffff;
                    }
                } else {
                    rst.ea_va = rst.pc_va + 8;
                }
            } else {
                rst.ea_va = 0;
            }


            // these need to be set (not just computed below) because these values
            // are used in read_noninstr_rec() for register records that are not part
            // of a chunk of instructions.
            lastRd = rst.instr >> 25 & 0x01F;

            // write some non-instruction recs?
            if (reservedCompress == NONINSTR_FOLLOWS) {
                write_noninstr = true;
                // rst.reservedCompress = 0;
            }

            write_rst2outbuf(&rst, 1);

            if (write_noninstr) {
                for (j = 0; j < noninstr_count_buf[noninstr_cnt]; j++) {

                    if (noninstr_buf[noninstr_buf_cnt].rtype == PREG_T) {
                        rstf_pregT* context = (rstf_pregT*) &noninstr_buf[noninstr_buf_cnt];
                        //pstate[context->primD].pstate = context->pstate;
                        pstate.pstate = context->pstate;
                    } else if (noninstr_buf[noninstr_buf_cnt].rtype == PAVADIFF_T) {
                        rstf_pavadiffT* pavadiff = (rstf_pavadiffT*) &noninstr_buf[noninstr_buf_cnt];
                        icontext = pavadiff->icontext;
                    } else if ( noninstr_buf[noninstr_buf_cnt].rtype == REGVAL_T ) {
                        // DECOMPRESS: setting proper register id
                        rstf_regvalT *rv = (rstf_regvalT*) &noninstr_buf[noninstr_buf_cnt];

                        // not the best solution, but these values don't make it into the trace
                        // so we're free to adopt a better solution later.  SH
                        if ( rv->regid[0] == 255 ) {
                            decDebugP( "[write_chunk()] Setting regid[0] to %u\n", lastRd );
                            rv->regid[0] = lastRd;
                        }
                        if ( rv->regid[1] == 255 ) {
                            decDebugP( "[write_chunk()] Setting regid[1] to %u\n", lastRd );
                            rv->regid[1] = lastRd;
                        }

                    }
                    write_rst2outbuf(&noninstr_buf[noninstr_buf_cnt], 1);
                    noninstr_buf_cnt++;
                }

                noninstr_cnt++;
            }
        }

        return num_instr + num_noninstr;
    }  // Rstunzip::write_chunk()

    // Read instr_buf[] to count number of (valid) ld/st instructions.
    int32_t find_num_ea() {
        int32_t i, ih;

        num_ea = 0;

        for (i = 0; i < num_instr; i++) {
            // ih = getIHash(instr_buf[i]); // removed - 20040210 (vp)

            // if (carry_ea(ih, get_flags(flags_buf[i], EA_VALID))) { // (replaced - 20040212 (vp)
            if (carry_ea(instr_buf[i], get_flags(flags_buf[i], EA_VALID))) {
                num_ea++;
            }
        }

        return num_ea;
    }  // Rstunzip::find_num_ea()

    // Read flags_buf[] to count RSRVD_CMPRSS=1 fields .
    int32_t find_num_noninstr_count() {
        int32_t i;

        num_noninstr_count = 0;

        for (i = 0; i < num_instr; i++) {
            if (get_flags(flags_buf[i], RSRVD_CMPRSS) == NONINSTR_FOLLOWS) {
                num_noninstr_count++;
            }
        }

        return num_noninstr;
    }  // Rstunzip::find_num_noninstr_count()

    // Sum noninstr_count_buf[].
    int32_t find_num_noninstr() {
        int32_t i;

        num_noninstr = 0;

        if (num_noninstr_count != 0) {
            num_noninstr = noninstr_count_buf[0];
        }

        for (i = 1; i < num_noninstr_count; i++) {
            num_noninstr += noninstr_count_buf[i];
        }

        return num_noninstr;
    }  // Rstunzip::find_num_instr()

    // Read n bytes from *inbuf_ptr into *to, and advance *inbuf_ptr.
    // Return the number of bytes read.
    int32_t read_inbuf(void* to, size_t n) {
        memcpy(to, inbuf_ptr, n);
        inbuf_ptr += n;

        return n;
    }  // Rstunzip::read_inbuf()

    // Write n RST records from from into outbuf_ptr, and advance
    // outbuf_ptr.  Return the number of records written.
    int32_t write_rst2outbuf(rstf_instrT* from, size_t n) {
        memcpy(outbuf_ptr, from, n * sizeof(rstf_instrT));
        outbuf_ptr += n;

        return n;
    }  // Rstunzip::write_rst2outbuf()

    int32_t read_ea(hash_table_t* table) {
        int8_t offset8;
        int16_t offset16;
        int32_t offset32;
        int64_t offset64;
        int32_t i, n;

        n = 0;

        if (num_ea > 0) {
            if (rtype == z_INSTR_T) {
                n = read_inbuf(ea_buf, num_ea * sizeof(uint64_t));

                //jan {
                //reading byte stream into uint64_t - need to swap_long on little endian
                for (int32_t j = 0; j < num_ea; j++) {
                    ea_buf[j] = SWAP_LONG(ea_buf[j]);
                }
                // jan }

            } else {
                for (i = 0; i < num_ea; i++) {
                    n += read_inbuf(&offset8, sizeof(offset8));

                    switch (offset8) {
                    case RESERVED_OFFSET_8BITS:
                        n += read_inbuf(&offset8, sizeof(offset8));
                        ea_buf[i] = table->ea_buf[i] + offset8;

                        offset_count[OFFSET_8BITS_RESERVED_IDX]++;
#ifdef _DEBUG0
                        fprintf(DBGFP, "(Reserved 8) offset8=0x%02hx ", offset8);
                        fprintf(DBGFP,
                                "base=0x%llx offset8=0x%02hx ea=0x%llx\n",
                                table->ea_buf[i], offset8, ea_buf[i]);
#endif
                        break;
                    case RESERVED_OFFSET_16BITS:
                        n += read_inbuf(&offset16, sizeof(offset16));
                        offset16 = SWAP_SHORT(offset16);  //jan
                        ea_buf[i] = table->ea_buf[i] + offset16;

                        offset_count[OFFSET_16BITS_IDX]++;
#ifdef _DEBUG0
                        fprintf(DBGFP, "(Reserved 16) offset8=0x%02hx ", offset8);
                        fprintf(DBGFP,
                                "base=0x%llx offset16=0x%04hx ea=0x%llx\n",
                                table->ea_buf[i], offset16, ea_buf[i]);
#endif
                        break;
                    case RESERVED_OFFSET_32BITS:
                        n += read_inbuf(&offset32, sizeof(offset32));
                        offset32 = SWAP_WORD(offset32);  //jan
                        ea_buf[i] = table->ea_buf[i] + offset32;

                        offset_count[OFFSET_32BITS_IDX]++;
#ifdef _DEBUG0
                        fprintf(DBGFP, "(Reserved 32) offset8=0x%02hx ", offset8);
                        fprintf(DBGFP,
                                "base=0x%llx offset32=0x%08x ea=0x%llx\n",
                                table->ea_buf[i], offset32, ea_buf[i]);
#endif
                        break;
                    case RESERVED_OFFSET_64BITS:
                        n += read_inbuf(&offset64, sizeof(offset64));
                        offset64 = SWAP_LONG(offset64);  //jan
                        ea_buf[i] = table->ea_buf[i] + offset64;

                        offset_count[OFFSET_64BITS_IDX]++;
#ifdef _DEBUG0
                        fprintf(DBGFP, "(Reserved 64) offset8=0x%02hx ", offset8);
                        fprintf(DBGFP,
                                "base=0x%llx offset64=0x%016llx ea=0x%llx\n",
                                table->ea_buf[i], offset64, ea_buf[i]);
#endif
                        break;
                    default:
                        ea_buf[i] = table->ea_buf[i] + offset8;

                        if (offset8 == 0) {
                            zero_offset_count++;
                        }

                        offset_count[OFFSET_8BITS_IDX]++;
#ifdef _DEBUG0
                        fprintf(DBGFP,
                                "base=0x%llx offset8=0x%02hx ea=0x%llx\n",
                                table->ea_buf[i], offset8, ea_buf[i]);
#endif
                    }
                }
            }
        }

        return n;
    }  // Rstunzip::read_ea()

    rstf_unionT read_noninstr_rec_rst;

    rstf_instrT* read_noninstr_rec() {
        uint8_t ccValue;
        bool setCC = false;
        read_noninstr_rec_rst.proto.rtype = *inbuf_ptr;

        decDebugP( "      %d) ", iCtr++ );

        if (read_noninstr_rec_rst.proto.rtype >= z0_PAVADIFF_T &&
                read_noninstr_rec_rst.proto.rtype <= z7_PAVADIFF_T) {

            decDebug( "decoded a z*_PAVADIFF_T\n" );

            switch (read_noninstr_rec_rst.proto.rtype) {
            case z0_PAVADIFF_T:
                memcpy(&read_noninstr_rec_rst, pava_cache.read(0),
                       sizeof(rstf_pavadiffT));
                pava_cache.update(0);
                break;
            case z1_PAVADIFF_T:
                memcpy(&read_noninstr_rec_rst, pava_cache.read(1),
                       sizeof(rstf_pavadiffT));
                pava_cache.update(1);
                break;
            case z2_PAVADIFF_T:
                memcpy(&read_noninstr_rec_rst, pava_cache.read(2),
                       sizeof(rstf_pavadiffT));
                pava_cache.update(2);
                break;
            case z3_PAVADIFF_T:
                memcpy(&read_noninstr_rec_rst, pava_cache.read(3),
                       sizeof(rstf_pavadiffT));
                pava_cache.update(3);
                break;
            case z4_PAVADIFF_T:
                memcpy(&read_noninstr_rec_rst, pava_cache.read(4),
                       sizeof(rstf_pavadiffT));
                pava_cache.update(4);
                break;
            case z5_PAVADIFF_T:
                memcpy(&read_noninstr_rec_rst, pava_cache.read(5),
                       sizeof(rstf_pavadiffT));
                pava_cache.update(5);
                break;
            case z6_PAVADIFF_T:
                memcpy(&read_noninstr_rec_rst, pava_cache.read(6),
                       sizeof(rstf_pavadiffT));
                pava_cache.update(6);
                break;
            case z7_PAVADIFF_T:
                memcpy(&read_noninstr_rec_rst, pava_cache.read(7),
                       sizeof(rstf_pavadiffT));
                pava_cache.update(7);
                break;
            default:
                fprintf(stderr, "Error: pava_cache.search() returned NOT_FOUND "
                        "in read_noninstr_rec()\n");
                exit(2);
            }

            total_pavadiff++;
            total_zpavadiff++;
            inbuf_ptr++;

        } else if ( compressedRegType( read_noninstr_rec_rst.proto.rtype ) ) {
            // DECOMPRESS
            if( read_noninstr_rec_rst.proto.rtype == z_REGPAIR_T ) {
                decDebug( "*** z_REGPAIR_T ***" );
                ++inbuf_ptr;
                decompressReg( *inbuf_ptr, 0 );
                decompressReg( *inbuf_ptr, 1 );
            } else {
                decompressReg( read_noninstr_rec_rst.proto.rtype, 0 );
            }

        } else {
            memcpy(&read_noninstr_rec_rst, inbuf_ptr, sizeof(rstf_pavadiffT));

            if (read_noninstr_rec_rst.proto.rtype == PAVADIFF_T) {
                total_pavadiff++;
                pava_cache.write(&read_noninstr_rec_rst.pavadiff);
            }

#ifdef DEC_DEBUG
            print_rstrec( stderr, &read_noninstr_rec_rst.instr );
#endif

            inbuf_ptr += sizeof(rstf_pavadiffT);
        }

        return &read_noninstr_rec_rst.instr;
    }  // Rstunzip::read_noninstr_rec()

    void decompressReg( uint8_t curr_rt, int32_t idx ) {

        int32_t regid = -1;
        int32_t rt = curr_rt;

        decDebug( "       " );
        if ( rt == z_REGID_T ) {
            regid = *++inbuf_ptr;
            decDebugP( "z_REGID_T id = %u\n", regid );
            decDebug( "       " );
            ++inbuf_ptr;
        }

        rt = *inbuf_ptr++;
        ValueCache::IdxT vcIdx;
        uint64_t value = 0;

        switch( rt ) {
        case z_REGVAL_64_T:
            memcpy( &value, inbuf_ptr, sizeof( uint64_t) );
            value = SWAP_LONG(value);
            inbuf_ptr += sizeof( uint64_t );
            vcIdx = valueCache.insert( value );
            decDebugP( "z_REGVAL_64_T value=%llu, ", value );
            decDebugP( "indexed to %u\n", vcIdx );
            break;
        case z_REGVAL_32_T:
        {
            uint32_t val32 = 0;
            memcpy( &val32, inbuf_ptr, sizeof( uint32_t ) );
            val32 = SWAP_WORD(val32);
            inbuf_ptr += sizeof( uint32_t );
            vcIdx = valueCache.insert( val32 );
            value = val32;
            decDebugP( "z_REGVAL_32_T value=%u, ", val32 );
            decDebugP( "indexed to %u\n", vcIdx );
        }
        break;
        case z_REGVAL_16_T:
        {
            uint16_t val16 = 0;
            memcpy( &val16, inbuf_ptr, sizeof( uint16_t ) );
            val16 = SWAP_SHORT(val16);
            inbuf_ptr += sizeof( uint16_t );
            value = val16;
            decDebugP( "z_REGVAL_16_T value=%llu\n", val16 );
        }
        break;
        case z_REGVAL_8_T:
        {
            uint8_t val8 = 0;
            memcpy( &val8, inbuf_ptr, sizeof( uint8_t ) );
            inbuf_ptr += sizeof( uint8_t );
            value = val8;
            decDebugP( "z_REGVAL_8_T value=%llu\n", val8 );
        }
        break;
        case z_VALUE_MINUS1_T:
            value = ~( 0x0ULL );
            decDebugP( "z_VALUE_MINUS1_T [%u]\n", z_VALUE_MINUS1_T );
            break;
        case z_VALUE_0_T:
            value = 0;
            decDebugP( "z_VALUE_0_T [%u]\n", z_VALUE_0_T );
            break;
        case z_VALUE_1_T:
            value = 1;
            decDebugP( "z_VALUE_1_T [%u]\n", z_VALUE_1_T );
            break;
        case z_VALUE_2_T:
            value = 2;
            decDebugP( "z_VALUE_2_T [%u]\n", z_VALUE_2_T );
            break;
        case z_VALUE_3_T:
            value = 3;
            decDebugP( "z_VALUE_3_T [%u]\n", z_VALUE_3_T );
            break;
        case z_VALUE_4_T:
            value = 4;
            decDebugP( "z_VALUE_4_T [%u]\n", z_VALUE_4_T );
            break;
        case z_VALUE_5_T:
            value = 5;
            decDebugP( "z_VALUE_5_T [%u]\n", z_VALUE_5_T );
            break;
        case z_VALUE_6_T:
            value = 6;
            decDebugP( "z_VALUE_6_T [%u]\n", z_VALUE_6_T );
            break;
        case z_VALUE_7_T:
            value = 7;
            decDebugP( "z_VALUE_7_T [%u]\n", z_VALUE_7_T );
            break;
        case z_VALUE_8_T:
            value = 8;
            decDebugP( "z_VALUE_8_T [%u]\n", z_VALUE_8_T );
            break;
        case z_REGIDX_T:
            memcpy( &vcIdx, inbuf_ptr, sizeof( ValueCache::IdxT ) ); // uint16_t
            I(sizeof( ValueCache::IdxT ) == sizeof( uint16_t ));
            vcIdx = SWAP_SHORT(vcIdx);
            inbuf_ptr += sizeof( ValueCache::IdxT );
            value = valueCache[vcIdx];
            decDebugP( "z_REGIDX_T, index = %u, ", vcIdx );
            decDebugP( "value = %llu\n", value );

            break;
        default:
            fprintf( stderr, "Found a record of type: " );
            print_rstrec( stderr, &read_noninstr_rec_rst.instr );
            assert( 0 );
        }
        decDebug( "       " );

        if( !idx ) {
            // Special cases when the index is 0

            if( *inbuf_ptr == z_CCR_T ) {
                ++inbuf_ptr;
                read_noninstr_rec_rst.regval.regtype[1] = RSTREG_CC_RT;
                read_noninstr_rec_rst.regval.regid[1] = *inbuf_ptr++;
                decDebugP( "z_CCR_T value = 0x%x\n", read_noninstr_rec_rst.regval.regid[1] );
            } else {
                read_noninstr_rec_rst.regval.regtype[1] = RSTREG_UNUSED_RT;
                read_noninstr_rec_rst.regval.regid[1] = 0;
            }
            read_noninstr_rec_rst.regval.rtype = REGVAL_T;
            read_noninstr_rec_rst.regval.postInstr = 1;
            read_noninstr_rec_rst.regval.reg64[1] = 0;
        }

        read_noninstr_rec_rst.regval.reg64[idx] = value;
        read_noninstr_rec_rst.regval.regtype[idx] = RSTREG_INT_RT;

        if( regid >= 0 ) {
            read_noninstr_rec_rst.regval.regid[idx] = regid;
        } else {
            read_noninstr_rec_rst.regval.regid[idx] = 255;
        }
        decDebugP( "[read_noninstr_rec()] Setting regid[%d] to ", idx );
        decDebugP( "%u\n", read_noninstr_rec_rst.regval.regid[idx] );
    }

    int32_t read_noninstr_recs() {
        int32_t i, n;

        n = 0;

        for (i = 0; i < num_noninstr; i++) {
            memcpy(&noninstr_buf[i], read_noninstr_rec(), sizeof(rstf_instrT));
            n += sizeof(rstf_instrT);
        }

        return n;
    }  // Rstunzip::read_noninstr_recs()

    int32_t zrecs_o() {
        return outbuf_ptr - outbuf;
    }  // Rstunzip::zrecs_o()

    int32_t zbytes_i() {
        return inbuf_ptr - inbuf;
    }  // Rstunzip::zbytes_i()

};  // class Rstunzip

// Compression class.
class Rstzipi : public RstzipBase {
public:
    rstf_instrT* inbuf_ptr;  // always points to current inbuf location
    uint8_t* outbuf_ptr;     // always points to current outbuf location

    Rstzipi()
    {
        zip_init();
    }

    ~Rstzipi() {
        zip_close();
    }

    void zip_init() {
        inbuf = NULL;
        inbuf_ptr = NULL;
        outbuf = NULL;
        outbuf_ptr = NULL;

        rstz_compress_prev_nrecs = 0;
        zip_chunk_chksm = 0;
        zip_chunk_chunk_counter = 0;
        in_same_chunk_prev_pc = 0;
        lastRID = -1;
        inChunk = false;
    }

    void zip_close() {
        //free(outbuf);
    }

    int32_t rstz_compress_prev_nrecs;
    // Compress nrecs RST records from rstbuf[] to rz2buf[].
    // Returns size of compressed nrecs records in rz2buf[].
    int32_t rstz_compress(uint8_t* rz2buf, rstf_unionT* rstbuf, int32_t nrecs) {
        int32_t buf_instr = 0;

        if (rstbuf == NULL) {
            return 0;
        }

        inbuf = &rstbuf[0].instr;
        outbuf = rz2buf;
        inbuf_ptr = inbuf;
        outbuf_ptr = outbuf;

        while (zrecs_i() < nrecs) {
            zip_chunk(nrecs);

            max_chunksize = MAX((int)max_chunksize, num_instr + num_noninstr);
            buf_instr += num_instr;
#if _DEBUG0
            print_chunk(DBGFP);
            if (num_instr != 0) {
                fprintf(DBGFP, "zrecs_i()=%d zbytes_o()=%d nrecs=%d\n\n",
                        zrecs_i(), zbytes_o(), nrecs);
            }
#endif
        }

        total_instr += buf_instr;
        total_noninstr += zrecs_i() - buf_instr;

        return zbytes_o();
    }  // Rstzip::zip()

#if 0
    int32_t rstz_write_prev_nrecs;
    // Compress nrecs RST records from buf[] to *outfp; assume exactly
    // nrecs records exist in buf[].  Returns number of records
    // compressed.
    int32_t rstz_write(rstf_unionT buf[], int32_t nrecs) {
        int32_t buf_instr = 0;

        inbuf = &buf[0].instr;

        if (outbuf == NULL || rstz_write_prev_nrecs < nrecs) {
            rstz_write_prev_nrecs = nrecs;

            free(outbuf);

            outbuf = (uint8_t*) malloc((nrecs+MAX_CHUNKSIZE) * sizeof(rstf_instrT));
            if (outbuf == NULL) {
                fprintf(stderr, "Error: could not allocate %d bytes "
                        "of memory in Rstzip::zip()\n",
                        (nrecs+MAX_CHUNKSIZE) * sizeof(rstf_instrT));
                exit(2);
            }
        }

        inbuf_ptr = inbuf;
        outbuf_ptr = outbuf;

        while (zrecs_i() < nrecs) {
            zip_chunk(nrecs);

            max_chunksize = MAX(max_chunksize, num_instr + num_noninstr);
            buf_instr += num_instr;
#if _DEBUG0
            print_chunk(DBGFP);
            if (num_instr != 0) {
                fprintf(DBGFP, "zrecs_i()=%d zbytes_o()=%d nrecs=%d\n\n",
                        zrecs_i(), zbytes_o(), nrecs);
            }
#endif
        }

        total_instr += buf_instr;
        total_noninstr += zrecs_i() - buf_instr;

        zfwrite(outbuf, zbytes_o());

        return zrecs_i();
    }  // Rstzip::zip()
#endif

protected:

    ValueCache valueCache;
    uint64_t currentPC;
    int32_t lastRID;
    bool inChunk;

    rstf_instrT* inbuf;      // input rst buffer
    uint8_t* outbuf;         // output buffer for compressed traces

    uint64_t zip_chunk_chksm;
    uint64_t zip_chunk_chunk_counter;
    // Compress one chunk of RST records from *inbuf_ptr to *outbuf_ptr.
    void zip_chunk(int32_t nrecs) {
        int32_t hashval, set;

        rstf_instrT* inbuf_start = inbuf_ptr;

#if 0
        // CURRDEBUG
        for( int32_t i = 0; i < 5; i++ ) {
            print_rstrec(DBGFP, inbuf_start++ );
        }
#endif

        read_chunk(nrecs);

        // Read_chunk() initializes pc_start to zero and will set pc_start
        // if a chunk is read.
        if (pc_start != 0 & num_instr > 0) {
            hashval = hash.hash(pc_start);
            set = hash.search(pc_start, num_instr, instr_buf, hashval);

            if (set == NOT_FOUND) {
                write_chunk();
                hash.write(pc_start, num_instr, instr_buf, num_ea, ea_buf, hashval);
                total_nonloop_chunk++;
            } else {
                write_loop_chunk(hash.read(set, hashval));
                hash.update(num_ea, ea_buf, hashval, set);
                total_loop_chunk++;
            }

            if (checksum_freq != 0) {
                zip_chunk_chksm += compute_checksum64();
                zip_chunk_chunk_counter++;

                if (zip_chunk_chunk_counter % checksum_freq == 0) {
                    write_outbuf(&zip_chunk_chksm, 8);
                    zip_chunk_chksm = 0;
                }
            }
        }
    }  // Rstzip::zip_chunk()

    int32_t make_pavadiff_rtype(int32_t index) {
        int32_t ztype;

        switch (index) {
        case 0:
            ztype = z0_PAVADIFF_T;
            break;
        case 1:
            ztype = z1_PAVADIFF_T;
            break;
        case 2:
            ztype = z2_PAVADIFF_T;
            break;
        case 3:
            ztype = z3_PAVADIFF_T;
            break;
        case 4:
            ztype = z4_PAVADIFF_T;
            break;
        case 5:
            ztype = z5_PAVADIFF_T;
            break;
        case 6:
            ztype = z6_PAVADIFF_T;
            break;
        case 7:
            ztype = z7_PAVADIFF_T;
            break;
        default:
            fprintf(stderr, "Error: index param > 7 in rstzip::make_pavadiff_rtype()\n");
            exit(2);
        }

        return ztype;
    }

    // Read one chunk of compressed data from *inbuf_ptr to instr_buf[],
    // ea_buf[], flags_buf[], etc.  Return the total number of records
    // written.
    int32_t read_chunk(int32_t nrecs) {
        rstf_instrT* inbuf_start;
        int32_t ih;
        int32_t lastRegid = -1;

        // init some data members
        rtype = 0;
        pc_start = 0;
        num_instr = 0;
        num_ea = 0;
        num_noninstr = 0;
        num_total = 0;
        num_noninstr_count = 0;

        memset(noninstr_count_buf, 0, MAX_CHUNKSIZE * sizeof(uint8_t));

        inbuf_start = inbuf_ptr;

        // find an INSTR_T record
        inChunk = false;
        while (zrecs_i() < nrecs && inbuf_ptr->rtype != INSTR_T) {
            //print_rstrec(DBGFP, inbuf_ptr);

            write_noninstr_rec(inbuf_ptr);
            inbuf_ptr++;
        }

        // inbuf_ptr points to an INSTR_T now
        if (zrecs_i() < nrecs && inbuf_ptr->rtype == INSTR_T) {
            //print_rstrec(DBGFP, inbuf_ptr);

            pc_start = inbuf_ptr->pc_va;

#ifdef COM_DEBUG
            static int32_t chunkNum = 0;
            fprintf( stderr, "writing chunk #%d\n", chunkNum++ );
#endif
            inChunk = true;
            while (zrecs_i() < nrecs && in_same_chunk(inbuf_ptr, nrecs, inbuf_ptr - inbuf_start + 1)) {
                if (inbuf_ptr->rtype == INSTR_T) {
                    // set instr_buf[]
                    instr_buf[num_instr] = inbuf_ptr->instr;

                    // save the destination register for regval records
                    // that follow this instruction.
                    lastRegid = inbuf_ptr->instr >> 25 & 0x01f;

                    // set ea_buf[]
                    // ih = getIHash(inbuf_ptr->instr); // removed - 20040210 (vp)

                    // if (carry_ea(ih, inbuf_ptr->ea_valid)) { // replaced - 20040210 (vp)
                    if (carry_ea(inbuf_ptr->instr, inbuf_ptr->ea_valid)) {
                        ea_buf[num_ea] = inbuf_ptr->ea_va;
                        num_ea++;
                    }

                    // set flags_buf[]
                    flags_buf[num_instr] = 0;
                    set_flags(&flags_buf[num_instr], NOTUSED, inbuf_ptr->notused);
                    set_flags(&flags_buf[num_instr], EA_VALID, inbuf_ptr->ea_valid);
                    set_flags(&flags_buf[num_instr], TR, inbuf_ptr->tr);
                    // set_flags(&flags_buf[num_instr], NOTUSED2, inbuf_ptr->notused2);
                    set_flags(&flags_buf[num_instr], NOTUSED2, 0);
                    set_flags(&flags_buf[num_instr], PR, inbuf_ptr->pr);
                    set_flags(&flags_buf[num_instr], BT, inbuf_ptr->bt);
                    set_flags(&flags_buf[num_instr], AN, inbuf_ptr->an);
                    set_flags(&flags_buf[num_instr], RSRVD_CMPRSS, INSTR_FOLLOWS);

                    if (num_instr == USHRT_MAX) {
                        fprintf(stderr, "Error: num_instr > USHRT_MAX in Rstzip::read_chunk()\n");
                        exit(2);
                    }

                    num_instr++;

                    if (noninstr_count_buf[num_noninstr_count]) {
                        num_noninstr_count++;
                    }
                } else {
                    if (noninstr_count_buf[num_noninstr_count] == LLONG_MAX) {
                        fprintf(stderr, "Error: num_noninstr > %d in Rstzip::read_chunk()\n", LLONG_MAX);
                        exit(2);
                    }

                    // copy non-instrustion record to noninstr_buf[]
                    memcpy(&noninstr_buf[num_noninstr], inbuf_ptr, sizeof(rstf_unionT));
                    lastRegIdBuf[num_noninstr] = lastRegid;

                    // increment noninstr_count_buf[]
                    noninstr_count_buf[num_noninstr_count]++;

                    set_flags(&flags_buf[num_instr - 1], RSRVD_CMPRSS, NONINSTR_FOLLOWS);
                    num_noninstr++;
                }

                inbuf_ptr++;
            }
#ifdef COM_DEBUG
            fprintf( stderr, "    wrote %d instructions\n", num_instr );
            fprintf( stderr, "    wrote %d non-instructions\n", num_noninstr );
#endif
        }

        if (num_instr > 0) {
            if (num_instr + num_noninstr < MAX_CHUNKSIZE) {
                chunksize_count[(num_instr + num_noninstr) / (MAX_CHUNKSIZE / CHUNKSIZE_RES)]++;
            } else {
                chunksize_count[CHUNKSIZE_RES - 1]++;
            }
        }

        return inbuf_ptr - inbuf_start;
    }  // Rstzip::read_chunk()

    uint64_t in_same_chunk_prev_pc;
    // Chunks are terminated by:
    //   1) non-sequential instructions
    //   2) end of buffer
    //   3) MAX_CHUNKSIZE
    //   4) > INT_MAX sequential noninstruction records
    // Chunks are not terminated by non-instruction records (to improve
    // compression).
    bool in_same_chunk(rstf_instrT* rst, int32_t nrecs, int32_t chunk_recs) {
        int32_t recs, ni_recs;

        if (chunk_recs >= MAX_CHUNKSIZE) {
            return false;
        } else if (rst->rtype == INSTR_T) {
            if (num_instr > 0) {
                if (rst->pc_va != in_same_chunk_prev_pc + 4) {
                    return false;
                }
            }

            in_same_chunk_prev_pc = rst->pc_va;
        } else {
            // find next rstf_instrT
            for (recs = zrecs_i(), ni_recs = 0;
                    recs < nrecs &&
                    ni_recs < INT_MAX &&
                    chunk_recs < MAX_CHUNKSIZE &&
                    rst->rtype != INSTR_T;
                    recs++, ni_recs++, chunk_recs++) {
                rst++;
            }

            if (recs < nrecs && chunk_recs < MAX_CHUNKSIZE) {
                if (rst->rtype != INSTR_T) {
                    return false;
                } else if (rst->pc_va != in_same_chunk_prev_pc + 4) {
                    return false;
                }
            } else {
                return false;
            }
        }

        return true;
    }  // Rstzip::in_same_chunk()

    // Write the chunk data from instr_buf[], ea_buf[], flags_buf[],
    // etc. to *outbuf_ptr.  Return the number of bytes written.
    int32_t write_chunk() {
        int32_t n = 0;

        rtype = z_INSTR_T;

        n += write_outbuf(&rtype, sizeof(rtype));
        n += write_outbuf(&num_instr, sizeof(num_instr));
        n += write_outbuf(&pc_start, sizeof(pc_start));

        n += write_outbuf(instr_buf, num_instr * sizeof(uint32_t));
        n += write_outbuf(flags_buf, num_instr * sizeof(flags_t));
        n += write_ea(NULL);
        n += write_outbuf(noninstr_count_buf, num_noninstr_count * sizeof(uint8_t));
        n += write_noninstr_recs();

        return n;
    }  // Rstzip::write_chunk()

    // Write the loop chunk data from instr_buf[], ea_buf[],
    // flags_buf[], etc. to *outbuf_ptr.  Return the number of bytes
    // written.
    int32_t write_loop_chunk(hash_table_t* table) {
        int32_t n = 0;

        rtype = zL_INSTR_T;

        n += write_outbuf(&rtype, sizeof(rtype));
        n += write_outbuf(&num_instr, sizeof(num_instr));
        n += write_outbuf(&pc_start, sizeof(pc_start));

        n += write_outbuf(flags_buf, num_instr * sizeof(flags_t));
        n += write_ea(table);
        n += write_outbuf(noninstr_count_buf, num_noninstr_count * sizeof(uint8_t));
        n += write_noninstr_recs();

        return n;
    }  // Rstzip::write_loop_chunk()

    int32_t write_ea(hash_table_t* table) {
        int8_t offset8;
        int16_t offset16;
        int32_t offset32;
        int64_t offset64;
        int32_t i, n;

        n = 0;

        // Nothing to write if num_ea == 0.
        if (num_ea > 0) {
            if (rtype == z_INSTR_T) {    // no compression
                n += write_outbuf(ea_buf, num_ea * sizeof(uint64_t));
            } else {                     // compression
                for (i = 0; i < num_ea; i++) {
                    offset64 = ea_buf[i] - table->ea_buf[i];

                    if (offset64 >= INT_MIN && offset64 <= INT_MAX) {
                        if (offset64 == RESERVED_OFFSET_8BITS ||
                                offset64 == RESERVED_OFFSET_16BITS ||
                                offset64 == RESERVED_OFFSET_32BITS ||
                                offset64 == RESERVED_OFFSET_64BITS) {
                            offset8 = RESERVED_OFFSET_8BITS;
                            n += write_outbuf(&offset8, sizeof(offset8));

                            offset_count[OFFSET_8BITS_RESERVED_IDX]++;
#ifdef _DEBUG0
                            fprintf(DBGFP, "(Reserved 8) offset8=0x%02hx ",
                                    table->ea_buf[i], offset8, ea_buf[i]);
#endif
                        } else {
                            offset_count[OFFSET_8BITS_IDX]++;

                            if (offset64 == 0) {
                                zero_offset_count++;
                            }
                        }

                        offset8 = (int8_t) offset64 & 0xff;
                        n += write_outbuf(&offset8, sizeof(offset8));
#ifdef _DEBUG0
                        fprintf(DBGFP, "base=0x%llx offset8=0x%02hx ea=0x%llx\n",
                                table->ea_buf[i], offset8, ea_buf[i]);
#endif
                    } else if (offset64 >= SHRT_MIN && offset64 <= SHRT_MAX) {
                        offset8 = RESERVED_OFFSET_16BITS;
                        offset16 = (int16_t) offset64 & 0xffff;
                        n += write_outbuf(&offset8, sizeof(offset8));
                        n += write_outbuf(&offset16, sizeof(offset16));

                        offset_count[OFFSET_16BITS_IDX]++;
#ifdef _DEBUG0
                        fprintf(DBGFP, "(Reserved 16) offset8=0x%02hx ", offset8);
                        fprintf(DBGFP, "base=0x%llx offset16=0x%04hx ea=0x%llx\n",
                                table->ea_buf[i], offset16, ea_buf[i]);
#endif
                    } else if (offset64 >= INT_MIN && offset64 <= INT_MAX) {
                        offset8 = RESERVED_OFFSET_32BITS;
                        offset32 = (int32_t) offset64 & 0xffffffff;
                        n += write_outbuf(&offset8, sizeof(offset8));
                        n += write_outbuf(&offset32, sizeof(offset32));

                        offset_count[OFFSET_32BITS_IDX]++;
#ifdef _DEBUG0
                        fprintf(DBGFP, "(Reserved 32) offset8=0x%02hx ", offset8);
                        fprintf(DBGFP,
                                "base=0x%llx offset32=0x%08x ea=0x%llx\n",
                                table->ea_buf[i], offset32, ea_buf[i]);
#endif
                    } else {
                        offset8 = RESERVED_OFFSET_64BITS;
                        n += write_outbuf(&offset8, sizeof(offset8));
                        n += write_outbuf(&offset64, sizeof(offset64));

                        offset_count[OFFSET_64BITS_IDX]++;
#ifdef _DEBUG0
                        fprintf(DBGFP, "(Reserved 64) offset8=0x%02hx ", offset8);
                        fprintf(DBGFP, "base=0x%llx offset64=0x%016llx ea=0x%llx\n",
                                table->ea_buf[i], offset64, ea_buf[i]);
#endif
                    }
                }
            }
        }

        return n;
    }  // Rstzip::write_ea()

    // Write n bytes from *from to *outbuf_ptr, and increment
    // outbuf_ptr.  Return the number of bytes written.
    int32_t write_outbuf(void* from, size_t n) {
        memcpy(outbuf_ptr, from, n);
        outbuf_ptr += n;

        return n;
    }  // Rstzip::write_outbuf()

    int32_t write_noninstr_rec(rstf_instrT* rst) {
        int32_t index, n;

        static int32_t niNr = 0;
        n = 0;


#ifdef COM_DEBUG
        print_rstrec( stderr, rst );
#endif

        comDebug( "      " );
        if (rst->rtype == PAVADIFF_T) {
            total_pavadiff++;
            index = pava_cache.search((rstf_pavadiffT*) rst);

            if (index == NOT_FOUND) {
                n = write_outbuf(rst, sizeof(rstf_instrT));
                pava_cache.write((rstf_pavadiffT*) rst);
            } else {
                uint8_t ztype = make_pavadiff_rtype(index);

                n = write_outbuf(&ztype, sizeof(uint8_t));
                pava_cache.update(index);

                total_zpavadiff++;
            }
            comDebug( "writing PAVADIFF_T\n" );
            // COMPRESS
        } else if ( rst->rtype == REGVAL_T ) {
            rstf_regvalT *regP = (rstf_regvalT*) rst;
            if ( isIntRegCompressable( regP ) ) {
                comDebug( "* Processing compressable register " );

                if( regP->regtype[1] == RSTREG_INT_RT ) {
                    uint8_t tag = z_REGPAIR_T;
                    comDebug( "[ *** z_REGPAIR_T *** ]" );
                    n += write_outbuf( &tag, sizeof( uint8_t ) );
                }

                n += compressIntegerRecord( regP->regid[0], regP->reg64[0], regP->cpuid, lastRID );

                if( regP->regtype[1] == RSTREG_INT_RT ) {
                    n += compressIntegerRecord( regP->regid[1], regP->reg64[1], regP->cpuid, lastRID );

                } else if( regP->regtype[1] == RSTREG_CC_RT ) {
                    n += writeCCRecord( regP->regid[1] );
                }

            } else {
                // we're not compressing this type of regval
                n = write_outbuf( rst, sizeof(rstf_instrT) );
                comDebug( "* Writing non-compressable REGVAL_T\n" );
            }
        } else {
            n = write_outbuf(rst, sizeof(rstf_instrT));
        }

        ++niNr;
        return n;
    }

    unsigned compressIntegerRecord( uint8_t regid, uint64_t regval, uint8_t cpuid, int32_t lastInstrRegId = -1 )
    {
        uint8_t tag;
        unsigned totalSize = 0;
        static uint8_t tagLookup[9] = { z_VALUE_0_T, z_VALUE_1_T, z_VALUE_2_T, z_VALUE_3_T,
                                        z_VALUE_4_T, z_VALUE_5_T, z_VALUE_6_T, z_VALUE_7_T, z_VALUE_8_T
                                      };
#ifdef COM_DEBUG
        static const char *tag2String[9] = { "z_VALUE_0_T", "z_VALUE_1_T", "z_VALUE_2_T", "z_VALUE_3_T",
                                             "z_VALUE_4_T", "z_VALUE_5_T", "z_VALUE_6_T", "z_VALUE_7_T", "z_VALUE_8_T"
                                           };
#endif

        comDebugP( "(last regid = %u)\n", lastInstrRegId );
        comDebug( "       " );


        // if we can't get the regid from the instruction, then
        // we'll need to output it to the compressed trace.
        if( regid != lastInstrRegId || !inChunk ) {
            comDebugP( "z_REGID_T id = %u\n       ", regid );
            tag = z_REGID_T;
            totalSize += write_outbuf( &tag, sizeof( uint8_t ) );
            totalSize += write_outbuf( &regid, sizeof( uint8_t ) );
        }

        ValueCache::IdxT idx;
        if( valueCache.hit( regval, idx ) ) {
            comDebugP( "z_REGIDX_T idx=%u ", idx );
            comDebugP( " val=%llu\n", regval );
            // if we hit in the value cache, then output the index.
            tag = z_REGIDX_T;
            totalSize += write_outbuf( &tag, sizeof( uint8_t ) );
            totalSize += write_outbuf( &idx, sizeof( ValueCache::IdxT ) );

        } else if ( regval < 9 ) {
            // we have specific records for values < 8
            tag = tagLookup[regval];
            totalSize += write_outbuf( &tag, sizeof( uint8_t ) );
            comDebugP( "%s ", tag2String[regval] );
            comDebugP( "[%u]\n", tag );

        } else if( (regval & 0x0ff) == regval ) {
            // if the regval can be expressed in one byte, then it's wasteful to
            // enter it in the value cache.   Instead, we output the 1-byte values
            // to the trace. Note: we NEVER put these value in the value cache!!!
            tag = z_REGVAL_8_T;
            totalSize += write_outbuf( &tag, sizeof( uint8_t ) );
            uint8_t rv8 = regval;
            totalSize += write_outbuf( &rv8, sizeof( uint8_t ) );
            comDebugP( "z_REGVAL_8_T val=%u\n", rv8 );

        } else if( (regval & 0x0ffff) == regval ) {
            // if the regval can be expressed in two bytes, then it's wasteful to
            // enter it in the value cache.   Instead, we output the 2-byte values
            // to the trace. Note: we NEVER put these value in the value cache!!!
            tag = z_REGVAL_16_T;
            totalSize += write_outbuf( &tag, sizeof( uint8_t ) );
            uint16_t rv16 = regval;
            totalSize += write_outbuf( &rv16, sizeof( uint16_t ) );
            comDebugP( "z_REGVAL_16_T val=%u\n", rv16 );

        } else if( regval == ~(0x0ULL) ) {
            tag = z_VALUE_MINUS1_T;
            totalSize += write_outbuf( &tag, sizeof( uint8_t ) );
            comDebugP( "z_VALUE_MINUS1_T [%u]", tag );

        } else {
            uint8_t size;
            ValueCache::IdxT i = valueCache.insert( regval );

            if( (regval & 0x0ffffffff ) == regval ) {
                tag = z_REGVAL_32_T;
                uint32_t rv32 = regval;
                totalSize += write_outbuf( &tag, sizeof( uint8_t ) );
                totalSize += write_outbuf( &rv32, sizeof( uint32_t ) );
                comDebugP( "z_REGVAL_32_T val=%u", rv32 );
            } else {
                tag = z_REGVAL_64_T;
                totalSize += write_outbuf( &tag, sizeof( uint8_t ) );
                totalSize += write_outbuf( &regval, sizeof( uint64_t ) );
                comDebugP( "z_REGVAL_64_T val=%llu", regval );
            }
            comDebugP( " indexed to %u\n", i );

        }

        comDebugP( "       size = %u\n", totalSize );

        return totalSize;
    }


    unsigned writeCCRecord( uint8_t CCRContents )
    {
        unsigned totalSize = 0;

        uint8_t tag = z_CCR_T;
        totalSize += write_outbuf( &tag, sizeof( uint8_t ) );
        totalSize += write_outbuf( &CCRContents, sizeof( uint8_t ) );
        comDebugP( "       [CCR] = 0x%x\n", CCRContents );

        return totalSize;
    }





    int32_t write_noninstr_recs() {
        int32_t i, n;

        n = 0;

        for (i = 0; i < num_noninstr; i++) {
            lastRID = lastRegIdBuf[i];
            n += write_noninstr_rec(&noninstr_buf[i]);
        }

        return n;
    }

    int32_t zrecs_i() {
        return inbuf_ptr - inbuf;
    }  // Rstzip::zrecs_i()()

    int32_t zbytes_o() {
        return outbuf_ptr - outbuf;
    }  // Rstzip::zbytes_o()()

};  // Rstzip

#endif  // _RSTZIP_H
